package Metier.Controleurs;

import Donnees.DaoProduit;
import Donnees.DaoStock;
import Metier.Exceptions.SuppressionDansStockException;
import Metier.Objets.Produit;
import Metier.Objets.Stock;
import Metier.Objets.TypeProduit;
import Presentation.FenetrePrincipale;
import Presentation.Onglet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;

/**
 * Contrôleur des stocks
 */
public class GestionStock {
    
    /**
     * Attributs
     */
    private final DaoStock daoStock;
    private final DaoProduit daoProduit;
    private final ResourceBundle configBundle;
    private final List<Produit> enAlerte;
    private final Onglet onglet;
    
    /**
     * Constructeur avec référence à l'onglet appelant
     * @param onglet l'onglet pour lequel ce gestionnaire travaille
     */
    public GestionStock(Onglet onglet) {
        this.daoStock = new DaoStock();
        this.daoProduit = new DaoProduit();
        this.onglet = onglet;
        if (onglet == null) {
            this.configBundle = ResourceBundle.getBundle("ressources/Stocks/Stocks", Locale.FRENCH);
        } else {
            this.configBundle = ResourceBundle.getBundle("ressources/Stocks/Stocks", onglet.getFP().getLocale());
        }
        enAlerte = new ArrayList<>();
    }
    
    /**
     * Permet de récupérer le contenu du tableau des stocks avec l'entête des colonnes et le contenu
     * @return le contenu de la table
     */
    public DefaultTableModel getContenuTableauGeneral() {
        DefaultTableModel tableModel = new DefaultTableModel();
        tableModel.addColumn(configBundle.getString("stock.produit"));
        tableModel.addColumn(configBundle.getString("stock.qteStock"));
        tableModel.addColumn(configBundle.getString("stock.addLot"));
        tableModel.addColumn(configBundle.getString("stock.supprLot"));
        tableModel.addColumn("id");
        Object[] rowdata = new Object[5];
        try {
            List<Produit> listProduits = daoProduit.chercher();
            
            for(Produit p : listProduits) {
                rowdata[0] = p.getNom();
                rowdata[1] = this.sommeQuantites(daoStock.chercherStockParIdProduit(p.getId()));
                rowdata[2] = configBundle.getString("stock.ajouter"); //le texte présent dans les ButtonColumn
                rowdata[3] = configBundle.getString("stock.supprimer");
                rowdata[4] = p.getId();
                
                tableModel.addRow(rowdata);
            }
        } catch (SQLException e) {
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errContenuTableStock"));
            e.printStackTrace();
        }
        return tableModel;
    }
    
    /**
     * Permet de récupérer le contenu de la table de suppression de stocks de boissons
     * @param aSupprimer le type de produit à supprimer
     * @return le contenu de la table
     */
    public DefaultTableModel getContenuTableauSupprBoisson(Produit aSupprimer) {
        DefaultTableModel tableModel = new DefaultTableModel();
        if(aSupprimer.getType() == TypeProduit.BOISSON){
            tableModel.addColumn(configBundle.getString("stock.boisson"));
            tableModel.addColumn(configBundle.getString("stock.qteLot"));
            tableModel.addColumn(configBundle.getString("stock.datePeremption"));
            tableModel.addColumn(configBundle.getString("stock.supprCombien"));
            tableModel.addColumn("id");
            Object[] rowdata = new Object[5];
            try {
                List<Stock> listLots = daoStock.chercherStockParProduit(aSupprimer);
                for(Stock s : listLots) {
                    rowdata[0] = aSupprimer.getNom();
                    rowdata[1] = s.getQuantite();
                    DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                    rowdata[2] = (s.getDatePeremption() != null ? df.format(s.getDatePeremption()) : configBundle.getString("stock.erreur"));
                    rowdata[3] = "";
                    rowdata[4] = s.getId();
                    tableModel.addRow(rowdata);
                }
            } catch (SQLException e) {
                FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errContenuTableSupprBoisson"));
                e.printStackTrace();
            }
        }
        else{
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.progErr"));
        }
        return tableModel;
    }

    /**
     * Somme la quantité de produits dans tous les lots d'un même produit.
     * @param liste la liste de stocks
     * @return la somme des quantités
     */
    public int sommeQuantites(List<Stock> liste){
        int idProduit = -1;
        if (!liste.isEmpty()) {
            idProduit = liste.get(0).getIdProduit();
        }
        int sum = 0;
        for(Stock s : liste){
            if(s.getIdProduit() == idProduit){
                sum += s.getQuantite();
            }
            else{
                return -1;
            }
        }
        return sum;
    }
    
    /**
     * Somme la quantité de produits dans tous les lots d'un même produit.
     * @param p le produit dont on veut connaitre la quantité
     * @return la somme des quantités
     */
    public int sommeQuantites(Produit p){
        List<Stock> liste;
        int sum = 0;
        try {
            liste = daoStock.chercherStockParProduit(p);
            for(Stock s : liste){
                sum += s.getQuantite();
            }
        } catch (SQLException e) {
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errSommeStocks"));
            e.printStackTrace();
        }
        return sum;
    }
    
    /**
     * Méthode revoyant tous les stocks associés au produit p
     * @param p le produit dont on veut les stocks
     * @return la liste de stocks du produit p
     */
    public List<Stock> getStock(Produit p) {
        try {
            return this.daoStock.chercherStockParProduit(p);
        } catch (SQLException e) {
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errGetStock"));
            e.printStackTrace();
        }
        return new ArrayList<>();
    }
    
    /**
     * Méthode renvoyant le Stock ayant l'id demandé
     * @param id id du stock voulu
     * @return le stock ayant l'id spécifié
     */
    public Stock getStockParId(int id) {
        Stock stock = null;
        try {
            stock = this.daoStock.chercherStockParId(id);
        } catch (SQLException e) {
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errGetStock"));
            e.printStackTrace();
        }
        return stock;
    }
    
    /**
     * Méthode d'ajout d'une quantité de produit à un stock de viennoiseries (pas de date de péremption)
     * @param p la viennoiserie a ajouter
     * @param quantite la quantité à ajouter
     */
    public void ajouterAuStock(Produit p, int quantite){
        if(quantite != 0 && p.getType() == TypeProduit.VIENNOISERIE){
            Stock s = this.getStock(p).get(0);
            s.ajouterAuStock(quantite);
            try {
                daoStock.modifier(s);
            } catch (SQLException e) {
                FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errAjoutViennoiserie"));
                e.printStackTrace();
            }
        }
        else if(quantite == 0){
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.qteNulle"));
        }
        else{
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.progErr"));
        }
    }
    
    /**
     * Méthode d'ajout d'un lot de boisson dans les stocks
     * @param p le type de produit a ajouter
     * @param quantite la quantité à ajouter
     * @param peremption la date de péremption du lot
     */
    public void ajouterAuStock(Produit p, int quantite, Date peremption){
        if(quantite != 0 && p.getType() == TypeProduit.BOISSON){
            if(peremption != null){
                Stock s = new Stock(-1, p.getId(), quantite, peremption);
                try {
                    s.setId(daoStock.ajouter(s));
                } catch (SQLException e) {
                    FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errAjoutBoisson"));
                    e.printStackTrace();
                }
            }
            else{
                FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.peremptionVide"));
            }
        }
        else if(quantite == 0){
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.qteNulle"));
        }
        else{
            this.ajouterAuStock(p,quantite);
        }
    }
    
    /**
     * Méthode de retrait d'une partie du stock d'une viennoiserie (1 seul lot pour ce type de produits)
     * @param p la viennoiserie dont on retire du stock
     * @param quantite la quantité à retirer
     */
    public void retirerAuStock (Produit p, int quantite){
        if(p.getType() == TypeProduit.VIENNOISERIE){
        Stock s = this.getStock(p).get(0);
            try {
                s.retirerAuStock(quantite);
                try {
                    daoStock.modifier(s);
                } catch (SQLException e) {
                    FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errSupprStock"));
                    e.printStackTrace();
                }
            } catch (SuppressionDansStockException e) {
                FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errStockInsuffisant") + " "+ e.getMessage());
                e.printStackTrace();
            }
        }
        else{
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.progErr"));
        }
    }

    /**
     * Méthode de retrait d'une partie de plusieurs lots contenant chacun une partie du stock de la même boisson
     * @param mapSupprBoisson map reliant l'id d'un stock à la quantité à enlever à ce stock
     */
    public void retirerAuStock(Map<Integer, Integer> mapSupprBoisson) {
        for(Entry<Integer, Integer> entry : mapSupprBoisson.entrySet()){
            Stock s = this.getStockParId(entry.getKey());
            try {
                s.retirerAuStock(entry.getValue());
                try {
                    if(s.getQuantite() != 0){
                        daoStock.modifier(s);
                    }
                    else{
                        daoStock.supprimer(s);
                    }
                } catch (SQLException e) {
                    FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errSupprStock"));
                    e.printStackTrace();
                }
            } catch (SuppressionDansStockException e) {
                FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errStockInsuffisant") + " "+ e.getMessage());
                e.printStackTrace();
            }
        }
    }
    
    /**
     * Cette méthode actualise la liste des produits en alerte et commande l'affichage des popups d'alerte
     */
    public void rafraichirAlertes(){
        List<Stock> stocks;
        try{
            stocks = daoStock.chercherStock();
            try{
                for(Produit p : daoProduit.chercher()){
                    if(p.getType() == TypeProduit.VIENNOISERIE){
                        for(Stock s : stocks){
                            if(s.getIdProduit() == p.getId()){
                                if(s.getQuantite() < p.getSeuilStock()){
                                    if(!enAlerte.contains(p)){
                                        enAlerte.add(p);
                                        this.affichePopupAlerte(p, s.getQuantite());
                                    }
                                }
                                else if(enAlerte.contains(p)){
                                    enAlerte.remove(p);
                                }
                            }
                        }
                    }
                    else{
                        List<Stock> sousListe = new ArrayList<>();
                        for(Stock s : stocks){
                            if(s.getIdProduit() == p.getId()){
                                sousListe.add(s);
                            }
                        }
                        int somme = this.sommeQuantites(sousListe);
                        if(somme < p.getSeuilStock()){
                            if(!enAlerte.contains(p)){
                                enAlerte.add(p);
                                this.affichePopupAlerte(p, somme);
                            }
                        }
                        else if(enAlerte.contains(p)){
                            enAlerte.remove(p);
                        }
                    }
                }
            } catch (SQLException e){
                FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errListeProduits"));
                e.printStackTrace();
            }
        } catch (SQLException e){
            FenetrePrincipale.afficherPopupErreur(configBundle.getString("stock.errContenuTableStock"));
            e.printStackTrace();
        }
    }
    
    /**
     * Affiche un popup d'alerte de stock insuffisant
     * @param p produit dont le stock est insuffisant
     */
    private void affichePopupAlerte(Produit p, int actuel){
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        JOptionPane.showMessageDialog(onglet,
                                configBundle.getString("stock.popupAlerteStock1")+p.getNom()+" "+configBundle.getString("stock.popupAlerteStock2")+p.getSeuilStock()+" "+configBundle.getString("stock.popupAlerteStock3")+actuel,
                                configBundle.getString("stock.avertissement"),
                                JOptionPane.WARNING_MESSAGE);
                    }
                });
            }
        });
        t.start();
    }
    
    /**
     * @return la liste de produits en alerte stock
     */
    public List<Produit> getListeAlertes(){
        return enAlerte;
    }
}
